import {
  defineComponent,
  h,
  VNodeArrayChildren,
  ref,
  PropType,
  reactive,
  watch,
} from 'vue';
import { useConvertMap } from '../composables/useConvertMap';
import GeoJSON from '@geo-maps/countries-coastline-2km5/map.geo.json';
import { clamp } from '@lagabu/tool';
export default defineComponent({
  name: 'WorldMap',
  props: {
    origin: {
      type: Array as PropType<number[]>,
      default: [180, 180],
    },
    scale: {
      type: Number,
      default: 1,
    },
  },
  setup(props) {
    const nations = useConvertMap(GeoJSON as any);
    const state = reactive({
      origin: props.origin,
      scale: props.scale,
    });

    watch([() => props.origin, () => props.scale], (newVal, oldVal) => {
      if (
        newVal[0] === oldVal[0] ||
        newVal[1] === oldVal[1] ||
        newVal[0] === state.origin ||
        newVal[1] === state.scale
      )
        return;
      state.origin = newVal[0] as number[];
      state.scale = newVal[1] as number;
    });

    const svg = ref<HTMLElement | null>(null);
    const wrapper = ref<SVGElement | null>(null);
    return {
      nations,
      svg,
      state,
      wrapper,
    };
  },
  data() {
    return {
      startDrag: false,
      startPos: [0, 0],
      tempOrigin: [0, 0],
    };
  },
  methods: {
    genPaths() {
      const children: VNodeArrayChildren = [];
      let strokeWidth = 0.4 / this.state.scale;
      for (let i = 0; i < this.nations.length; i++) {
        let n = this.nations[i];
        children.push(
          h('path', {
            stroke: 'inherit',
            'stroke-width': strokeWidth,
            d: n.path,
            key: 'nation-' + i,
          })
        );
      }
      if (this.$slots.default) {
        children.push(this.$slots.default());
      }

      return h(
        'g',
        {
          ref: 'svg',
          'transform-origin': `${this.state.origin[0]} ${this.state.origin[1]}`,
          transform: `translate(${-1 * (this.state.origin[0] - 180)} ${
            -1 * (this.state.origin[1] - 180)
          }) scale(${this.state.scale})`,
          style: {
            transition: 'all 600ms ease',
            'transform-origin': `${(this.state.origin[0] * 100) / 360}% ${
              (this.state.origin[1] * 100) / 360
            }%`,
            filter: 'drop-shadow(-10px 10px 10px var(--light-cyan))',
          },
        },
        children
      );
    },
    onDrag(e: MouseEvent) {
      if (!this.startDrag) return;
      const svg: SVGGElement = this.svg as any;
      if (!svg) return;
      // const { width, height } = svg.getBBox(),
      //   bodyWidth = document.body.clientWidth,
      //   bodyHeight = document.body.clientHeight;
      const tempOrigin = [
         (e.clientX - this.startPos[0]) / this.state.scale,
         (e.clientY - this.startPos[1]) / this.state.scale,
      ];
      const origin = [
        -1 * (this.state.origin[0] - 180) + tempOrigin[0],
        -1 * (this.state.origin[1] - 180) - tempOrigin[1],
      ];
      svg.setAttribute(
        'transform',
        `translate(${origin[0]} ${origin[1]}) scale(${this.state.scale})`
      );
      this.tempOrigin = [
        this.state.origin[0] - tempOrigin[0],
        this.state.origin[1] + tempOrigin[1],
      ];
    },
    onDragEnd() {
      this.startDrag = false;
      if (!this.wrapper) return;
      this.state.origin = [...this.tempOrigin];
      this.wrapper.removeEventListener('mousemove', this.onDrag, false);
      this.wrapper.removeEventListener('mouseup', this.onDragEnd, false);
    },
    onMousedown(e: MouseEvent) {
      this.startDrag = true;
      this.startPos = [e.clientX, e.clientY];
      this.tempOrigin = [...this.state.origin];
      if (!this.wrapper) return;
      this.wrapper.addEventListener('mousemove', this.onDrag, false);
      this.wrapper.addEventListener('mouseup', this.onDragEnd, false);
    },
    onWheel(e: WheelEvent) {
      if (this.startDrag) return;
      const delta = e.deltaY > 0 ? -1 : 1;
      let x = e.clientX,
        y = e.clientY,
        g: SVGGElement,
        gH: number,
        gW: number;
      if (!(g = e.currentTarget as any)) return;
      g = (g as any).children[0];
      if (g.tagName !== 'g') return;
      const { left, top, width, height } = g.getBoundingClientRect();
      let gBox = g.getBBox();
      gH = gBox.height;
      gW = gBox.width;
      x = gW * ((x - left) / width) + gBox.x;
      y = gH * (1 - (y - top) / height) + gBox.y;
      x = clamp(x, 0, 360);
      y = clamp(y, 0, 360);
      this.state.scale = clamp(delta * 0.2 + this.state.scale, 1, 100);
      this.state.origin = [x, y];
      this.$emit('wheel:update', {
        scale: this.state.scale,
        origin: this.state.origin,
      });
    },
    genSvg() {
      return h(
        'svg',
        {
          ref: 'wrapper',
          class: 'world-map',
          xmlns: 'http://www.w3.org/2000/svg',
          version: '1.1',
          viewBox: '0 0 360 360',
          fill: 'transparent',
          onWheel: this.onWheel,
          onMousedown: this.onMousedown,
          style: {
            cursor: 'grab',
          },
        },
        this.genPaths()
      );
    },
  },
  render() {
    return h(
      'div',
      {
        class: 'world-map__wrapper',
      },
      this.genSvg()
    );
  },
});
